package CS4310.csueb.visual;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import javax.sound.midi.*;
import javax.swing.*;

import CS4310.csueb.characters.Boss;
import CS4310.csueb.characters.Character;
import CS4310.csueb.characters.Enemy;
import CS4310.csueb.characters.Hero;
import CS4310.csueb.items.Armor;
import CS4310.csueb.items.Food;

/**
 * Implementation of the BattleFrame class, which is the Battle Engine for the game.
 * 
 * @author Bikas Satpathy
 *
 */

public class BattleFrame extends JDialog implements ActionListener{
        
        private static final long serialVersionUID = 1L;
        
        private JPanel battleWindow, itemPanel;
        private JButton attack, item, flee;
        private JTextArea battleText;
        private JScrollPane scroller, itemScroll;
        private JProgressBar bossHealth, heroHealth;
        private JLabel heroLabel, enemyLabel;
        private Hero hero;
        private Enemy enemy;
        private HeroSprite hSprite;
        private CharacterSprite cSprite;
        private int turn = 1;
        private JDialog itemFrame, outcome;
        
        private Sequence s;             //for playing background music
        private Sequencer seq;
        
        public BattleFrame(JFrame frame, Hero h, Enemy e) {
                
                super(frame,"BATTLE!", true);
                
                hero = h;
                enemy = e;
                
                playMusic();
                buildFrames();
        }
        
        public BattleFrame(JFrame frame, HeroSprite h, CharacterSprite c) {
                super(frame, "BATTLE!", true);
                
                Character charac = c.getCharSheet();
                if (charac instanceof Enemy) {
                        hero = h.getHeroSheet();
                        enemy = (Enemy) charac;
                }
                hSprite = h;
                cSprite = c;
                
                playMusic();
                buildFrames();
        }
        
        private void buildFrames() {
                
                //create the battle frame, using same dimensions as regular frame.
                //battle frame should not close unless win, lose, or flee.
        		//this.setUndecorated(true); //remove title Frame
        		//can't remove window deco (Minimize) from Java 
        		this.setSize(800, 600);
                this.setResizable(false);
                this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
                //this.setAlwaysOnTop(true);
                this.setLocationRelativeTo(null);
                
                //really ugly way to set the frame as unmovable (center window each time it is moved)
                this.addComponentListener(new ComponentListener() {
                                public void componentMoved(ComponentEvent e) {setLocationRelativeTo(null);}
                                public void componentHidden(ComponentEvent arg0){}
                                public void componentResized(ComponentEvent arg0) {}
                                public void componentShown(ComponentEvent arg0) {}}); 

                //The panel where the sprites, health bars, and buttons placed
                battleWindow = new JPanel();
                battleWindow.setBackground(Color.BLUE);
                battleWindow.setLayout(null);
                this.add(battleWindow);
                
                //buttons to choose when battling
                attack = new JButton ("ATTACK");
                battleWindow.add(attack);
                
                attack.setBounds(0, 425, 200, 50);
                item = new JButton ("USE ITEM");
                battleWindow.add(item);
                item.setBounds (0, 475, 200, 50);
                flee = new JButton ("FLEE");
                battleWindow.add(flee);
                flee.setBounds(0, 525, 200, 50);
                attack.addActionListener(this);
                item.addActionListener(this);
                flee.addActionListener(this);
                
                //Text are is for displaying battle logs (how much damage is done, etc)
                //and might get replaced by the list of usable items when user clicks "Use Item"
                battleText = new JTextArea();
                battleText.setEditable(false);
                scroller = new JScrollPane(battleText);
                scroller.setBounds(200, 425, 595, 150);
                battleWindow.add(scroller);
                
                //Testing and placing hero/enemy sprite locations, health bars and labels
                
                //Hero and boss sprite placement
                ImageIcon bossIcon = new ImageIcon(cSprite.getSpriteImg());
                JLabel boss = new JLabel();
                boss.setIcon(bossIcon);
                battleWindow.add(boss);
                boss.setBounds(575, 90, 375, 320);
                
                ImageIcon heroIcon = new ImageIcon(hSprite.getSpriteImg());
                JLabel hero = new JLabel();
                hero.setIcon(heroIcon);
                battleWindow.add(hero);
                hero.setBounds(90, 90, 375, 320);
                
                //name labels for hero and enemy
                heroLabel = new JLabel(this.hero.toString() + ", Level: " + this.hero.getLevel());
                battleWindow.add(heroLabel);
                heroLabel.setBounds(5, 10, 140, 100);
                
                enemyLabel = new JLabel(this.enemy.toString() + ", Level: " + this.enemy.getLevel());
                battleWindow.add(enemyLabel);
                enemyLabel.setBounds(430, 10, 140, 100);
                
                //Health bars for hero and boss         
                heroHealth = new JProgressBar(0, this.hero.getMaxHP());
                heroHealth.setValue(this.hero.getCurrHP());
                battleWindow.add(heroHealth);
                heroHealth.setBounds(150, 25, 200, 50);
                heroHealth.setForeground(Color.GREEN);
                heroHealth.setBackground(Color.WHITE);
                heroHealth.setString("HP:  " + this.hero.getCurrHP() + "/" + this.hero.getMaxHP());
                heroHealth.setStringPainted(true);
                
                bossHealth = new JProgressBar(0, this.enemy.getMaxHP());
                bossHealth.setValue(this.enemy.getMaxHP());
                battleWindow.add(bossHealth);
                bossHealth.setBounds(575, 25, 200, 50);
                bossHealth.setForeground(Color.GREEN);
                bossHealth.setBackground(Color.WHITE);  
                
                updateHealthBars();
                this.setVisible(true);
        }
        
        public void actionPerformed(ActionEvent e) {
                int damage = 1;
                int bossItemUsed = 0; //see if boss used item or not.
                
                if (e.getActionCommand() == "ATTACK") {
                        
                        if (enemy instanceof Boss && bossHealth.getValue() < enemy.getMaxHP() * 0.2) {
                                if (((Boss) enemy).getItem() == null){
                                        //do nothing if Boss already used its item
                                }
                                else {
                                        bossItemUsed = 1;
                                        battleText.append("Turn " + this.turn + ": " + enemy.toString() + " had some "
                                                        + ((Boss) enemy).getItem().toString() + " and restored "
                                                        + ((Boss) enemy).getItem().getPower() + " HP.\n");
                                        bossHealth.setValue(bossHealth.getValue() + ((Boss) enemy).getItem().getPower());
                                        ((Boss) enemy).setItem(null);
                                        updateHealthBars();
                                
                                        damage = damageCalc(hero, enemy);
                                        //add to battle log
                                        battleText.append("Turn " + this.turn + ": You did "+ damage +" damage to " 
                                                        + enemy.toString() + "\n");
                                        bossHealth.setValue(Math.max(0, bossHealth.getValue() - damage));
                        
                                        //check if either enemy has died
                                        if (bossHealth.getValue() <= 0) {
                                                displayVictoryScreen();
                                                return;
                                        }
                                        updateHealthBars();
                                }
                        }
                        
                        if (hero.getSpeed() >= enemy.getSpeed() && bossItemUsed == 0) { //hero attacks first
                                damage = damageCalc(hero, enemy);
                                //add to battle log
                                battleText.append("Turn " + this.turn + ": You did "+ damage +" damage to " 
                                                + enemy.toString() + "\n");
                                bossHealth.setValue(Math.max(0, bossHealth.getValue() - damage));
                        
                                //check if either enemy has died
                                if (bossHealth.getValue() <= 0) {
                                        displayVictoryScreen();
                                        return;
                                }
                                updateHealthBars();
                                
                                damage = damageCalc(enemy, hero);
                                hero.setCurrHP(-1 * damage);
                                heroHealth.setValue(Math.max(0, hero.getCurrHP()));
                                heroHealth.setString("HP:  " + this.hero.getCurrHP() + "/" + this.hero.getMaxHP());
                                battleText.append("Turn " + this.turn + ": " + enemy.toString() + " did "+ damage +
                                                " damage to you.\n");
                                
                                if (heroHealth.getValue() <= 0) {
                                        displayDefeatScreen();
                                        return;
                                }
                                updateHealthBars();
                        }
                        else if (enemy.getSpeed() > hero.getSpeed() && bossItemUsed == 0) { //enemy attacks first
                                damage = damageCalc(enemy, hero);
                                hero.setCurrHP(-1 * damage);
                                heroHealth.setValue(Math.max(0, hero.getCurrHP()));
                                heroHealth.setString("HP:  " + this.hero.getCurrHP() + "/" + this.hero.getMaxHP());
                                battleText.append("Turn " + this.turn + ": " + enemy.toString() + " did "+ damage +
                                                " damage to you.\n");
                        
                                if (heroHealth.getValue() <= 0) {
                                        displayDefeatScreen();
                                        return;
                                }
                                updateHealthBars();
                                
                                battleText.append("Turn " + this.turn + ": You did "+ damage +" damage to "
                                                + enemy.toString() + "\n");
                                bossHealth.setValue(Math.max(0, bossHealth.getValue() - damage));
                                
                                if (bossHealth.getValue() <= 0) {
                                        displayVictoryScreen();
                                        return;
                                }
                                updateHealthBars();
                        }
                        turn++;
                        bossItemUsed = 0;
                }
                else if (e.getActionCommand().equals("FLEE")) {
                        stopMusic();
                        
                        outcome = new JDialog(this, "FLEE!", true);
                        outcome.add(new JLabel("You fled the battle"));
                        outcome.setSize(200, 200);
                        outcome.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
                        outcome.setLocationRelativeTo(this);
                        outcome.setVisible(true);
                        this.dispose();
                }
                else if (e.getActionCommand().equals("USE ITEM")) {
                        displayItemFrame();
                }
                else if (e.getActionCommand().equals("Cancel")) {
                        itemFrame.setVisible(false);
                        this.setVisible(true);
                }
                else {
                        Food consumed = null;
                        
                        for(Food f : hero.getFood()) {
                                if(f.toString().equals(e.getActionCommand()))
                                {
                                        consumed = f;
                                        break;
                                }
                        }
                        hero.getFood().remove(consumed);
                        
                        itemFrame.dispose();
                        battleText.append("Turn " + this.turn + ": " + hero.toString() + " had some " + consumed.toString() +
                                        " and ");
                        
                        if (consumed.getType() == 'h') { //restore health
                                //System.out.println("Restored HP" + consumed.getPower());
                                battleText.append("restored " + consumed.getPower() + " HP.\n");
                                heroHealth.setValue(Math.max(hero.getMaxHP(), hero.getCurrHP() + consumed.getPower()));
                                hero.setCurrHP(consumed.getPower());
                        }
                        else if (consumed.getType() == 'a') { //boost attack
                                battleText.append("boosted attack power by " + consumed.getPower() + " %.\n");
                                hero.setAttack((int) Math.ceil(hero.getAttack() * consumed.getPower() / 100));
                                //System.out.println("attack is now:" + hero.getAttack());
                        }
                        else if (consumed.getType() == 'd') { //boost defense
                                battleText.append("boosted defense power by " + consumed.getPower() + " %.\n");
                                hero.setDefense((int) Math.ceil(hero.getDefense() * consumed.getPower() / 100));
                                //System.out.println("defense is now:" + hero.getDefense());
                        }
                        else if (consumed.getType() == 's') { //boost attack
                                battleText.append("boosted speed by " + consumed.getPower() + " %.\n");
                                hero.setSpeed((int) Math.ceil(hero.getSpeed() * consumed.getPower() / 100));
                                //System.out.println("Speed is now:" + hero.getSpeed());
                        }
                        
                        this.setVisible(true);
                        damage = damageCalc(enemy, hero);
                        hero.setCurrHP(-1 * damage);
                        heroHealth.setValue(Math.max(0, hero.getCurrHP()));
                        heroHealth.setString("HP:  " + this.hero.getCurrHP() + "/" + this.hero.getMaxHP());
                        battleText.append("Turn " + this.turn + ": " + enemy.toString() + " did "+ damage +
                                        " damage to you.\n");
                        
                        if (heroHealth.getValue() <= 0) {
                                displayDefeatScreen();
                                return;
                        }
                        updateHealthBars();
                }
        }
        
        //Calculate battle damage based on stats. Base 13.4% damage +/- ratio of attack power/defense power     
        private int damageCalc(Character attacker, Character attackee) {
                return (int) Math.ceil(0.134 * attackee.getMaxHP() * attacker.getAttack()/attackee.getDefense());
        }
        
        private void displayVictoryScreen() {
                //display a "Victory frame"
                stopMusic();
                
                outcome = new JDialog(this, "VICTORY!", true);
                outcome.add(new JLabel("You Defeated " + enemy.toString()));
                outcome.setSize(new Dimension (200, 200));
                outcome.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
                outcome.setLocationRelativeTo(null);
                outcome.setVisible(true);
                this.dispose();
        }
        
        private void displayDefeatScreen() {
                //display a "defeated frame"
                stopMusic();
                
                outcome = new JDialog(this,"DEFEAT!", true);
                outcome.add(new JLabel("You were defeated by " + enemy.toString()));
                outcome.setSize(200, 200);
                outcome.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
                outcome.setLocationRelativeTo(null);
                outcome.setVisible(true);
                this.dispose();
        }
        
        private void displayItemFrame() {
                itemFrame = new JDialog(this, "Use Item", true);
                itemFrame.setSize(400, 290);
                itemFrame.setResizable(false);
                itemPanel = new JPanel();
                itemPanel.setBackground(Color.RED);
                itemPanel.setLayout(null);
                itemFrame.add(itemPanel);
                JButton cancel = new JButton("Cancel");
                cancel.setBounds(145,215, 100, 40);
                cancel.addActionListener(this);
                itemPanel.add(cancel);
                itemFrame.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
                listFood();
                itemFrame.setLocationRelativeTo(null);                
                itemFrame.addComponentListener(new ComponentListener() {
                    public void componentMoved(ComponentEvent e) {itemFrame.setLocationRelativeTo(null);}
                    public void componentHidden(ComponentEvent arg0){}
                    public void componentResized(ComponentEvent arg0) {}
                    public void componentShown(ComponentEvent arg0) {}});
                itemFrame.setVisible(true);
        }
        
        private void listFood() {
                JPanel list = new JPanel();
                list.setLayout(new FlowLayout(FlowLayout.LEFT));
                list.setPreferredSize(new Dimension (350, 200));
                JLabel label;
                JButton button;
                int numButtons = 0;
                
                for (Food f : hero.getFood()) {
                        if (f.getType() == 'h') {
                                label = new JLabel(f.toString() + ": Restore " + (int) f.getPower() + " HP.");
                                numButtons++;
                        }
                        else if (f.getType() == 'a') {
                                label = new JLabel(f.toString() + ": Boost attack by " + (int) f.getPower() + " %.");
                                numButtons++;
                        }
                        else if (f.getType() == 'd') {
                                label = new JLabel(f.toString() + ": Boost defense by " + (int) f.getPower() + " %.");
                                numButtons++;
                        }
                        else {
                                label = new JLabel(f.toString() + ": Boost speed by " + (int) f.getPower() + " %.");
                                numButtons++;
                        }
                        
                        list.add(label);
                        button = new JButton("Use Item");
                        button.setSize(50, 31);
                        button.addActionListener(this);
                        button.setActionCommand(f.toString());
                        //System.out.println(button.getHeight());
                        list.setPreferredSize(new Dimension (350, (int) Math.max(200, numButtons * button.getHeight() + 5)));
                        list.add(button);
                        //list.revalidate();
                }
                
                itemScroll = new JScrollPane(list);
                itemScroll.setBounds(17,5, 350, 200);
                itemScroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
                itemPanel.add(itemScroll);
        }
        
        private void playMusic() {
                try {
                        //create a midi sequence from a .mid file
                s = MidiSystem.getSequence(new File("CS4310/csueb/audio/GaryBattle.mid"));
        
                // Create a sequencer, make it loop forever until window closes (battle ends)
                seq = MidiSystem.getSequencer();
                seq.open();
                seq.setSequence(s);
                seq.setLoopCount(Sequencer.LOOP_CONTINUOUSLY);
            
                // Play the midi file
                seq.start();
            } 
                catch (MalformedURLException e) {} 
                catch (IOException e) {} 
                catch (MidiUnavailableException e) {} 
                catch (InvalidMidiDataException e) {}   
        }
        
        private void stopMusic() {
                seq.stop();
        }
        
        private void updateHealthBars() {
                int heroHP = hero.getCurrHP();
                int maxHP = hero.getMaxHP();
                
                int bossHP = bossHealth.getValue();
                int maxBossHP = bossHealth.getMaximum();
                
                if(heroHP >= Math.ceil(maxHP * 0.8)){
                        heroHealth.setForeground(Color.GREEN);
                }
                else if(heroHP < Math.ceil(maxHP * 0.8) && heroHP >= Math.ceil(maxHP/2)){
                        heroHealth.setForeground(Color.YELLOW);
                }
                else if(heroHP < Math.ceil(maxHP/2) && heroHP >= Math.ceil(maxHP/4)){
                        heroHealth.setForeground(Color.ORANGE);
                }
                else if(heroHP < Math.ceil(maxHP/4)){
                        heroHealth.setForeground(Color.RED);
                }
        
                if(bossHP >= Math.ceil(maxBossHP * 0.8)){
                        bossHealth.setForeground(Color.GREEN);
                }
                else if(bossHP <= Math.ceil(maxBossHP * 0.8) && bossHP >= Math.ceil(maxBossHP/2)){
                        bossHealth.setForeground(Color.YELLOW);
                }
                else if(bossHP < Math.ceil(maxBossHP/2) && bossHP >= Math.ceil(maxBossHP/4)){
                        bossHealth.setForeground(Color.ORANGE);
                }
                else if(bossHP < Math.ceil(maxBossHP/4)){
                        bossHealth.setForeground(Color.RED);
                }
        }
        
        public static void main(String[] args)
        {
                HeroSprite h = new HeroSprite("CS4310/csueb/visual/img/Male_Left_BattleFrame.png" ,"Bill Westin");
                h.getHeroSheet().collectItem(new Food());
                h.getHeroSheet().collectItem(new Food("Wild Fruit", 25, 'h'));
                /*h.collectItem(new Food("club soda", 15, 'a'));
                h.collectItem(new Food("candy bar", 15, 'd'));
                h.collectItem(new Food("bread crumbs", 15, 's'));
                h.collectItem(new Food("cheese pizza", 20, 's'));
                h.collectItem(new Food("hot wings", 20, 'a'));
                h.collectItem(new Food("fried chicken", 20, 'd'));
                h.collectItem(new Food("gatorade", 20, 'h'));*/
                
                CharacterSprite c = new CharacterSprite("CS4310/csueb/visual/img/Male_Left_BattleFrame.png", new Boss("Boss Bad Guy"));
                
                new BattleFrame(new JFrame(),h, c);
        }
}